/*
 * Copyright (c) Kumo Inc. and affiliates.
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <unordered_map>

#include <melon/cportability.h>
#include <melon/optional.h>
#include <melon/io/io_buf.h>
#include <melon/io/io_buf_queue.h>
#include <melon/json/dynamic.h>

/* This is an implementation of the BSER binary serialization scheme.
 * BSER was created as a binary, local-system-only representation of
 * JSON values.  It is more space efficient in its output text than JSON,
 * and cheaper to decode.
 * It has no requirement that string values be UTF-8.
 * BSER was created for use with Watchman.
 * https://facebook.github.io/watchman/docs/bser.html
 */

namespace melon {
    namespace bser {
        class MELON_EXPORT BserDecodeError : public std::runtime_error {
        public:
            using std::runtime_error::runtime_error;
        };

        enum class BserType : int8_t {
            Array = 0,
            Object,
            String,
            Int8,
            Int16,
            Int32,
            Int64,
            Real,
            True,
            False,
            Null,
            Template,
            Skip,
        };

        extern const uint8_t kMagic[2];

        struct serialization_opts {
            serialization_opts();

            // Whether to sort keys of object values before serializing them.
            // Note that this is potentially slow and that it does not apply
            // to templated arrays defined via defineTemplate; its keys are always
            // emitted in the order defined by the template.
            bool sort_keys;

            // incremental growth size for the underlying Appender when allocating
            // storage for the encoded output
            size_t growth_increment;

            // BSER allows generating a more space efficient representation of a list of
            // object values.  These are stored as an "object template" listing the keys
            // of the objects ahead of the objects themselves.  The objects are then
            // serialized without repeating the key string for each element.
            //
            // You may use the templates field to associate a template with an
            // array.  You should construct this map after all mutations have been
            // performed on the Dynamic instance that you intend to serialize as bser,
            // as it captures the address of the Dynamic to match at encoding time.
            // https://facebook.github.io/watchman/docs/bser.html#array-of-templated-objects
            using TemplateMap = std::unordered_map<const melon::Dynamic *, melon::Dynamic>;
            melon::Optional<TemplateMap> templates;
        };

        // parse a BSER value from a variety of sources.
        // The complete BSER data must be present to succeed.
        melon::Dynamic parseBser(melon::StringPiece);

        melon::Dynamic parseBser(melon::ByteRange);

        melon::Dynamic parseBser(const melon::IOBuf *);

        // When reading incrementally, it is useful to know how much data to
        // read to fully decode a BSER pdu.
        // Throws std::out_of_range if more data needs to be read to decode
        // the header, or throws a runtime_error if the header is invalid
        size_t decodePduLength(const melon::IOBuf *);

        melon::kmstring toBser(melon::Dynamic const &, const serialization_opts &);

        std::unique_ptr<melon::IOBuf> toBserIOBuf(
            melon::Dynamic const &, const serialization_opts &);
    } // namespace bser
} // namespace melon

/* vim:ts=2:sw=2:et:
 */
